package com.oj.controller;

import com.oj.config.OJConfig;
import com.oj.entity.*;
import com.oj.exception.OJException;
import com.oj.mapper.PrivilegeMapper;
import com.oj.mapper.SolutionMapper;
import com.oj.mapper.SolutionSourceMapper;
import com.oj.mapper.ContestProblemMapper;
import com.oj.service.ProblemService;
import com.oj.service.SolutionService;
import com.oj.service.SolutionSourceService;
import com.oj.service.UserService;
import com.oj.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;

import static com.oj.util.CodeMsg.*;

@Slf4j
@Controller
@RequestMapping
public class SubmitController {
    @Autowired
    private OJConfig ojConfig;

    @Autowired
    private SolutionMapper solutionMapper;

    @Autowired
    private SolutionService solutionService;

    @Autowired
    private ProblemService problemService;

    @Autowired
    private UserService userService;

    @Autowired
    private SolutionSourceMapper solutionSourceMapper;

    @Autowired
    private ContestProblemMapper contestProblemMapper;

    @Autowired
    private PrivilegeMapper privilegeMapper;

    @GetMapping("/ide")
    public ModelAndView toIde(){
        List<Language> langs = OJUtil.getActiveLanguages("GUET");
        ModelAndView mv = new ModelAndView();
        mv.addObject("languages", langs);
        mv.setViewName("problem/ide.html");
        return mv;
    }

    @ResponseBody
    @RequestMapping(value = {"/api/admin/rejudge"})
    public Result<String> rejudge(@RequestBody @Validated(value = {Submit.updateValid.class}) Submit submit,
                                 HttpServletRequest request, HttpServletResponse response, Model model){
        //log.info(submit.toString());
        if (submit.getSolutionId() <= 0) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }
        if (JudgeUtil.JudgeRequest(ojConfig.getJudgerIp(), ojConfig.getJudgerPort(), submit.getSolutionId()) != 0){
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
        return Result.success();
    }

    @ResponseBody
    @RequestMapping(value = {"/api/submit"})
    public Result<String> submit(@RequestBody @Validated(value = {Submit.updateValid.class}) Submit submit,
                                 HttpServletRequest request, HttpServletResponse response, Model model){
        if (request.getSession().getAttribute("session_username") == null) {
            return Result.error(CodeMsg.USER_NOT_LOGIN.locale());
        }
        String username = (String) request.getSession().getAttribute("session_username").toString();
        if (username == null) {
            return Result.error(CodeMsg.USER_NOT_LOGIN.locale());
        }
        Date dt_prevSubmit = (Date)request.getSession().getAttribute("session_submit");
        Date dt = new Date();
        if(dt_prevSubmit!=null){
            if(dt.getTime()-dt_prevSubmit.getTime() < 3000){
                return Result.error(CodeMsg.SUBMIT_TOO_FREQUENTLY.locale());
            }
        }
        request.getSession().setAttribute("session_submit", dt);

        Solution solution = new Solution();
        solution.setUsername(username);
        solution.setLanguage(submit.getLanguage());
        solution.setLanguage_name(OJUtil.getLanguageName("GUET", submit.getLanguage()));

        solution.setContest_id(submit.getContestId());
        if (solution.getContest_id() != 0) {
            ContestProblem cp = contestProblemMapper.query(solution.getContest_id(), submit.getProblemId());
            if (cp == null) {
                return Result.error(CodeMsg.INNER_FAULT.locale());
            }
            solution.setProblem_id(cp.getProblem_id());
        } else {
            solution.setProblem_id(Integer.parseInt(submit.getProblemId()));
        }

        SimpleDateFormat simpleDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        solution.setSubmit_date(Timestamp.valueOf(simpleDate.format(new Date())));
        solution.setCode_length(submit.getSource().length());
        Integer n = solutionMapper.insert(solution);
        if (n != 1) {
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }

        SolutionSource solutionSource = new SolutionSource();
        solutionSource.setSolution_id(solution.getSolution_id());
        solutionSource.setSource(submit.getSource());
        solutionSourceMapper.insert(solutionSource);

        if (JudgeUtil.JudgeRequest(ojConfig.getJudgerIp(), ojConfig.getJudgerPort(), solution.getSolution_id()) != 0){
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
        return Result.success(solution.getSolution_id().toString());
    }

    @ResponseBody
    @RequestMapping(value = {"/api/submit/test"})
    public Result<String> submitTest(@RequestBody @Validated(value = {Submit.updateValid.class}) Submit submit,
                                     HttpServletRequest request, HttpServletResponse response, Model model){
        String username = null;
        if (request.getSession().getAttribute("session_username") != null) {
            username = (String) request.getSession().getAttribute("session_username").toString();
        }

        Date dt_prevSubmit = (Date)request.getSession().getAttribute("session_submit");
        Date dt = new Date();
        if(dt_prevSubmit!=null){
            if(dt.getTime()-dt_prevSubmit.getTime() < 3000){
                return Result.error(CodeMsg.SUBMIT_TOO_FREQUENTLY.locale());
            }
        }
        request.getSession().setAttribute("session_submit", dt);

        JudgeTest testBean = new JudgeTest();
        testBean.setCode(submit.getSource());
        testBean.setInput(submit.getInput());
        testBean.setLanguage_id(submit.getLanguage().toString());
        testBean.setProblem_id("0");

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String sessionId = MD5Util.MD5(username+df.format(new Date())+ new Random().nextInt(1000)).toString();
        testBean.setSession_id(sessionId);

        String resultJson = null;
        try {
            resultJson = JSON.toJSONString(testBean);
        } catch (Exception e) {
            // TODO: handle exception
            log.error("json serialize failed.");
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }

        if (JudgeUtil.JudgeTestRequest(ojConfig.getJudgerIp(), ojConfig.getJudgerPort(), resultJson) != 0){
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
        return Result.success(sessionId);
    }

    @ResponseBody
    @RequestMapping(value = {"/api/submit/verify"})
    public Result<String> submitVerify(@RequestBody @Validated(value = {Submit.updateValid.class}) Submit submit,
                                     HttpServletRequest request, HttpServletResponse response, Model model){
        User user = userService.getUserBySession(request);
        if (user == null
                || (privilegeMapper.query(user.getUsername(), "HEAD") == null
                && privilegeMapper.query(user.getUsername(), "ADMIN") == null)) {
            throw new OJException(CodeMsg.PROBLEM_NO_SUCH);
        }
        String username = user.getUsername();
        Date dt_prevSubmit = (Date)request.getSession().getAttribute("session_submit");
        Date dt = new Date();
        if(dt_prevSubmit!=null){
            if(dt.getTime()-dt_prevSubmit.getTime() < 3000){
                return Result.error(CodeMsg.SUBMIT_TOO_FREQUENTLY.locale());
            }
        }
        request.getSession().setAttribute("session_submit", dt);

        JudgeTest testBean = new JudgeTest();
        testBean.setCode(submit.getSource());
        testBean.setLanguage_id(submit.getLanguage().toString());
        testBean.setProblem_id(submit.getProblemId());

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String sessionId = MD5Util.MD5(username+df.format(new Date())+ new Random().nextInt(1000)).toString();
        testBean.setSession_id(sessionId);

        String resultJson = null;
        try {
            resultJson = JSON.toJSONString(testBean);
        } catch (Exception e) {
            // TODO: handle exception
            log.error("json serialize failed.");
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }

        if (JudgeUtil.JudgeVerifyRequest(ojConfig.getJudgerIp(), ojConfig.getJudgerPort(), resultJson) != 0){
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
        return Result.success(sessionId);
    }
    @ResponseBody
    @RequestMapping(value = {"/api/submit/check"})
    public Result<JudgeTest> submitChcek(@RequestParam Map<String, Object> map, HttpServletRequest request) {
        if (map.get("sessionId") == null) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }
        String sessionId = map.get("sessionId").toString();

        String path = ojConfig.getJudgeLogPath() + "judge-log-"+ sessionId + ".json";
        File file = new File(path);
        if(!file.exists()){
            JudgeTest result = new JudgeTest();
            result.setSession_id(sessionId);
            result.setVerdict("pending");
            return Result.success(result);
        }

        String result_json = StreamHandler.readEx(file);
        JudgeTest result = JSON.parseObject(result_json, JudgeTest.class);

        /* verify problem， change output with judge log */
        if (Integer.parseInt(result.getProblem_id()) > 0) {
            String pathDetail = ojConfig.getJudgeLogPath() + "judge-log-"+ sessionId + ".log";
            File fileDetail = new File(pathDetail);
            if(fileDetail.exists()){
                result.setOutput(StreamHandler.readEx(fileDetail));
            }
        }
        return Result.success(result);
    }

    @ResponseBody
    @GetMapping("/api/languages")
    public Result<List> languages(){
        List<Language> langs = OJUtil.getActiveLanguages("GUET");
        return Result.success(langs);
    }

    @ResponseBody
    @GetMapping("/api/submit/template")
    public Result<String> template(Integer language){
        return Result.success(OJUtil.getCodeTemplateByLanguageId(language));
    }
}
